package ssen.mvc.base {
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;

import ssen.mvc.core.IContextDispatcher;

public class ContextDispatcher implements IContextDispatcher {
	private static var _globalDispatcher:IEventDispatcher=new EventDispatcher;
	private var _eventDispatcher:IEventDispatcher;
	private var _parent:IContextDispatcher;

	public function ContextDispatcher(parent:IContextDispatcher=null) {
		_eventDispatcher=new EventDispatcher;
		_parent=parent;

		startup();
	}

	private function startup():void {
		if (_parent) {
			_parent.eventDispatcher.addEventListener(ContextEvent.FROM_PARENT, catchOutsideEvent);
		}
		_globalDispatcher.addEventListener(ContextEvent.FROM_GLOBAL, catchOutsideEvent);
		_eventDispatcher.addEventListener(ContextEvent.FROM_CHILD, catchOutsideEvent);
	}

	private function catchOutsideEvent(event:ContextEvent):void {
		_eventDispatcher.dispatchEvent(event.evt);

		if (event.penetrate) {
			if (event.type === ContextEvent.FROM_CHILD) {
				dispatch(event.evt, DispatchTarget.PARENT, true);
			} else if (event.type === ContextEvent.FROM_PARENT) {
				dispatch(event.evt, DispatchTarget.CHILDREN, true);
			}
		}
	}

	private function shutdown():void {
		if (_parent) {
			_parent.eventDispatcher.removeEventListener(ContextEvent.FROM_PARENT, catchOutsideEvent);
		}
		_globalDispatcher.removeEventListener(ContextEvent.FROM_GLOBAL, catchOutsideEvent);
		_eventDispatcher.removeEventListener(ContextEvent.FROM_CHILD, catchOutsideEvent);
	}

	public function get eventDispatcher():IEventDispatcher {
		return _eventDispatcher;
	}

	public function get parentEventDispatcher():IContextDispatcher {
		return _parent;
	}

	public function createChild():IContextDispatcher {
		return new ContextDispatcher(this);
	}

	public function dispatch(event:Event, to:String="self", penetrate:Boolean=false):void {
		var dispatcher:IEventDispatcher;

		if (to == DispatchTarget.CHILDREN) {
			_eventDispatcher.dispatchEvent(new ContextEvent(ContextEvent.FROM_PARENT, event, penetrate));
		} else if (to == DispatchTarget.ALL) {
			_globalDispatcher.dispatchEvent(new ContextEvent(ContextEvent.FROM_GLOBAL, event, penetrate));
		} else if (to == DispatchTarget.PARENT) {
			_parent.eventDispatcher.dispatchEvent(new ContextEvent(ContextEvent.FROM_CHILD, event, penetrate));
		} else if (to == DispatchTarget.SELF) {
			_eventDispatcher.dispatchEvent(event);
		} else {
			throw new Error("unknown dispatch target :: " + to);
		}
	}

	public function dispose():void {
		shutdown();
		_eventDispatcher=null;
		_parent=null;
	}
}
}
